﻿using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
using Autodesk.AutoCAD.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WCAD
{
    public static class EmEntityMethod
    {
        public static void AddEntities<T>(List<T> ents) where T : Entity
        {
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction tran = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)db.BlockTableId.GetObject(OpenMode.ForRead);
                BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].GetObject(OpenMode.ForWrite);
                foreach (var item in ents)
                {
                    var id = btr.AppendEntity(item);
                    tran.AddNewlyCreatedDBObject(item, true);
                }
                tran.Commit();
            }
        }
        public static void AddEntity(Entity ent)
        {
            AddEntities(new List<Entity>() { ent });
        }
        public static Extents3d GetExtents3d<T>(List<T> ents) where T : Entity
        {
            ents = ents.FindAll(x => x.Bounds != null);
            double minx = ents.Min(x => x.Bounds.Value.MinPoint.X);
            double miny = ents.Min(x => x.Bounds.Value.MinPoint.Y);
            double maxx = ents.Max(x => x.Bounds.Value.MaxPoint.X);
            double maxy = ents.Max(x => x.Bounds.Value.MaxPoint.Y);
            return new Extents3d(new Point3d(minx, miny, 0), new Point3d(maxx, maxy, 0));
        }
        public static Point3d GetCenterPoint<T>(List<T> ents) where T : Entity
        {
            double minx = ents.Min(x => x.GeometricExtents.MinPoint.X);
            double miny = ents.Min(x => x.GeometricExtents.MinPoint.Y);
            double maxx = ents.Max(x => x.GeometricExtents.MaxPoint.X);
            double maxy = ents.Max(x => x.GeometricExtents.MaxPoint.Y);
            return new Point3d(minx/2+maxx/2,miny/2+maxy/2,0);
        }
        public static void EraseEntities<T>(List<T> ents) where T : Entity
        {
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction tran = db.TransactionManager.StartTransaction())
            {
                foreach (var item in ents)
                {
                    item.ObjectId.GetObject(OpenMode.ForWrite);
                    if (item.IsErased) continue;
                    if (item.ObjectId == ObjectId.Null) continue;
                    item.Erase();
                }
                tran.Commit();
            }
        }
        public static void EraseEntity<T>(T ent) where T : Entity
        {
            EraseEntities(new List<T>() { ent });
        }
        public static List<Entity> ExplodeEntity<T>(T ent) where T : Entity
        {
            DBObjectCollection dbs = new DBObjectCollection();
            ent.Explode(dbs);
            List<Entity> entities = new List<Entity>();
            foreach (Entity item in dbs)
            {
                entities.Add(item);
            }
            return entities;
        }
        public static void EditEntities<T>(List<T> entity, Action action) where T : Entity
        {
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction tran = db.TransactionManager.StartTransaction())
            {
                entity.ForEach(e => e.ObjectId.GetObject(OpenMode.ForWrite));
                action();
                tran.Commit();
            }
        }
        public static void EditEntity(Entity entity, Action action)
        {
            EditEntities(new List<Entity>() { entity }, action);
        }
        public static bool IsOverlapping(Entity entity1, Entity entity2)
        {
            Point3dCollection pos = new Point3dCollection();
            entity1.IntersectWith(entity2, Intersect.OnBothOperands, pos, IntPtr.Zero, IntPtr.Zero);
            return pos.Count > 0;
        }
        public static bool IsSameColor(Entity entity1, Entity entity2)
        {
            bool bb = entity1.Color == entity2.Color;
            return bb;
        }
        public static void Move(this Entity entity,Point3d point1,Point3d point2)
        {
            Matrix3d matrix=Matrix3d.Displacement(point2- point1);
            if (entity.IsNewObject)
            {
                entity.TransformBy(matrix);
            }
            else
            {
                Database database = HostApplicationServices.WorkingDatabase;
                using(Transaction tran = database.TransactionManager.StartTransaction())
                {
                    entity.ObjectId.GetObject(OpenMode.ForWrite);
                    entity.TransformBy(matrix);
                    tran.Commit();
                }
            }
        }

        public static List<List<T>> GroupEntities<T>(List<T> entities, Func<Entity, Entity, bool> func) where T : Entity
        {
            List<List<T>> result = new List<List<T>>();
            List<Friend> friends = new List<Friend>();
            for (int i = 0; i < entities.Count; i++)
            {
                Friend friend = new Friend(i);
                friends.Add(friend);
            }
            for (int i = 0; i < entities.Count - 1; i++)
            {
                for (int j = i + 1; j < entities.Count; j++)
                {

                    if (func(entities[i], entities[j]))
                    {
                        friends[i].Friends.Add(friends[j]);
                        friends[j].Friends.Add(friends[i]);
                    }
                }
            }

            while (friends.Count > 0)//整个朋友池还没清空的时候，一直循环，表示还有新的组没有被分
            {
                List<T> list = new List<T>();//建一个组
                Queue<Friend> queue = new Queue<Friend>();//新建一个队列
                queue.Enqueue(friends[0]);//队列尾部加入friends的第一个
                friends.RemoveAt(0);//friends移除第一个
                while (queue.Count > 0)//当队列数量大于0时。一直循环，因为代表这一个组的成员还没有找全
                {
                    Friend friend = queue.Dequeue();//从队列头部弹出一个friend
                    list.Add(entities[friend.Id]);//把这个人对应的T加到分组中
                    foreach (Friend f in friend.Friends)//遍历他所有的朋友
                    {
                        if (friends.Contains(f))//如果朋友池中还有他的朋友，说明之前还没有被别人选择过
                        {
                            queue.Enqueue(f);//把这个朋友加入队列尾部
                            friends.Remove(f);//把这个朋友从朋友池中剔除，不让他被别的人选择
                        }
                    }
                }
                result.Add(list);//整个队列清空之后，说明这一组人员已经到齐， 把这一组添加到结果里
            }

            return result;
        }

        public static List<List<Entity>> GroupEntities(List<Entity> entities)
        {
            List<List<Entity>> result = new List<List<Entity>>();
            List<Friend> friends = new List<Friend>();
            for (int i = 0; i < entities.Count; i++)
            {
                Friend friend = new Friend(i);
                friends.Add(friend);
            }
            for (int i = 0; i < entities.Count - 1; i++)
            {
                for (int j = i + 1; j < entities.Count; j++)
                {

                    if (IsOverlapping(entities[i], entities[j]))
                    {
                        friends[i].Friends.Add(friends[j]);
                        friends[j].Friends.Add(friends[i]);
                    }
                }
            }

            while (friends.Count > 0)//整个朋友池还没清空的时候，一直循环，表示还有新的组没有被分
            {
                List<Entity> list = new List<Entity>();//建一个组
                Queue<Friend> queue = new Queue<Friend>();//新建一个队列
                queue.Enqueue(friends[0]);//队列尾部加入friends的第一个
                friends.RemoveAt(0);//friends移除第一个
                while (queue.Count > 0)//当队列数量大于0时。一直循环，因为代表这一个组的成员还没有找全
                {
                    Friend friend = queue.Dequeue();//从队列头部弹出一个friend
                    list.Add(entities[friend.Id]);//把这个人对应的Entity加到分组中
                    foreach (Friend f in friend.Friends)//遍历他所有的朋友
                    {
                        if (friends.Contains(f))//如果朋友池中还有他的朋友，说明之前还没有被别人选择过
                        {
                            queue.Enqueue(f);//把这个朋友加入队列尾部
                            friends.Remove(f);//把这个朋友从朋友池中剔除，不让他被别的人选择
                        }
                    }
                }
                result.Add(list);//整个队列清空之后，说明这一组人员已经到齐， 把这一组添加到结果里
            }

            return result;
        }
        class Friend
        {
            public int Id { get; set; }//序列号
            public List<Friend> Friends { get; set; }//朋友们
            public Friend(int id)
            {
                Id = id;
                Friends = new List<Friend>();
            }
        }
    }
}
